package xtrace

import (
	"context"
	"gitee.com/git-lz/twelve/common/consts"
	"net/url"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/opentracing/opentracing-go"
	"github.com/pkg/errors"
	"go.elastic.co/apm"
)

// XTracer 包一层
type XTracer struct {
	opentracing.Tracer
}

func (*XTracer) Close() error {
	apm.DefaultTracer.Close()
	opentracing.SetGlobalTracer(opentracing.NoopTracer{})
	return nil
}

func parseUrls(serviceUrls string) ([]*url.URL, error) {
	var urls []*url.URL
	for _, field := range strings.Split(serviceUrls, ",") {
		field = strings.TrimSpace(field)
		if field == "" {
			continue
		}
		u, err := url.Parse(field)
		if err != nil {
			return nil, errors.Wrapf(err, "解析APM服务地址失败 %s", serviceUrls)
		}
		urls = append(urls, u)
	}
	if len(urls) == 0 {
		return nil, errors.Wrapf(nil, "APM服务地址解析为空 %s", serviceUrls)
	}
	return urls, nil
}

// TraceIdFromContext 从context中获取TraceId
func TraceIdFromContext(ctx context.Context) string {
	if ctx != nil {
		if ginCtx, ok := ctx.(*gin.Context); ok {
			ctx = ginCtx.Request.Context()
		}
	}
	transaction := apm.TransactionFromContext(ctx)
	if transaction != nil {
		return transaction.TraceContext().Trace.String()
	}
	span := apm.SpanFromContext(ctx)
	if span != nil {
		return span.TraceContext().Trace.String()
	}

	traceId, ok := ctx.Value(consts.TraceIdKey).(string)
	if !ok {
		return ""
	}

	return traceId
}

// NewCtxWithParentSpanAndType 从旧context中获取span，注入到新的context中返回，原本没有span，则根据ctxType新生成一个
func NewCtxWithParentSpanAndType(ctx context.Context, ctxType string) context.Context {
	if ctx != nil {
		if ginCtx, ok := ctx.(*gin.Context); ok {
			ctx = ginCtx.Request.Context()
		}
	}
	transaction := apm.TransactionFromContext(ctx)
	if transaction == nil {
		return NewCtxWithSpan(context.Background(), ctxType)
	}
	return apm.DetachedContext(ctx)
}

// NewCtxWithSpan 生成注入span的ctx
// ctxType 建议填写ctx将要串起来的任务的类型 例如 http-request、kafka-msg
func NewCtxWithSpan(ctx context.Context, ctxType string) context.Context {
	traceId := TraceIdFromContext(ctx)
	if traceId != "" {
		return ctx
	}

	span, newCtx := opentracing.StartSpanFromContext(ctx, ctxType)
	defer span.Finish()
	return newCtx
}

func ExtractTrueCtx(ctx context.Context) context.Context {
	if ctx != nil {
		if ginCtx, ok := ctx.(*gin.Context); ok {
			newCtx := ginCtx.Request.Context()
			return context.WithValue(newCtx, consts.SyncContextKey, ginCtx.Copy())
		}
	}
	return ctx
}

func NewGoroutineCtx(ctx context.Context) context.Context {
	return context.WithValue(context.Background(), consts.TraceIdKey, TraceIdFromContext(ctx))
}
